<html>
<head>
<link rel="shortcut icon" href="./favicon.ico">
<link rel="stylesheet" type="text/css" href="./style.css">
<link rel="canonical" href="./Address_Translator_Static.html">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="*This is the fixed-range version of the [Arithmetic Address Translator](./Address_Translator_Arithmetic.html).*">
<title>Address Translator Static</title>
</head>
<body>

<p class="inline bordered"><b><a href="./Address_Translator_Static.v">Source</a></b></p>
<p class="inline bordered"><b><a href="./legal.html">License</a></b></p>
<p class="inline bordered"><b><a href="./index.html">Index</a></b></p>

<h1>Address Translator (Static)</h1>
<p><em>This is the fixed-range version of the <a href="./Address_Translator_Arithmetic.html">Arithmetic Address
 Translator</a>.</em></p>
<p>Translates a <em>fixed</em>, arbitrary, unaligned range of N locations into an
 aligned range (starting at zero, up to N-1) so the translated address can
 be used to sequentially index into other addressed components
 (multiplexers, address decoders, RAMs, etc...).</p>
<p>When memory-mapping a small memory or a number of control registers to
 a base address that isn't a power-of-2, the Least Significant Bits (LSBs)
 which vary over the address range will not address the mapped entries in
 order. This addressing offset scrambles the order of the control registers
 and of the memory locations so that the mapped order no longer matches the
 physical order of the actual hardware, which makes design and debugging
 harder.</p>
<p>If the address range does not start at a power-of-2 boundary, the LSBs will
 not count in strictly increasing order and will not start at zero: the
 order of the numbers the LSBs represent is rotated by the offset to the
 nearest power-of-2 boundary. Also, if the address range does not span
 a power-of-2 sized block, then not all possible LSB values will be present.</p>
<p>However, we can describe a translation table as a small asynchronous
 read-only memory which translates only the raw LSBs into consecutive LSBs
 to then directly address the memory or control registers. A separate
 <a href="./Address_Decoder_Behavioural.html">Address Decoder</a> signals when the
 translation is valid.</p>
<p>For example, take 4 locations at addresses 7 to 10, which we would like to
 map to a range of 0 to 3. We want address 7 to access the zeroth location,
 and so on. To map 4 locations, the address' two LSBs must be translated as
 follows:</p>
<pre>
01<b>11</b> (7)  --> 01<b>00</b> (4)
10<b>00</b> (8)  --> 10<b>01</b> (5)
10<b>01</b> (9)  --> 10<b>10</b> (6)
10<b>10</b> (10) --> 10<b>11</b> (7)
</pre>

<p>You can see the raw two LSBs are in the sequence 3,0,1,2, which we must map
 to 0,1,2,3. We can pre-fill a table with the right values to do that, where
 address 3 will contain value 0, address 0 will contain value 1, etc...
 Translating the LSBs like so re-aligns the addresses to the nearest
 power-of-2 boundary, leaving the LSBs in the order we need.</p>
<p>Typically, you'll need this Address Translator alongside an Address Decoder
 module which decodes a <em>fixed</em> address range: either
 <a href="./Address_Decoder_Static.html">Static</a>, or
 <a href="./Address_Decoder_Behavioural.html">Behavioural</a> or
 <a href="./Address_Decoder_Arithmetic.html">Arithmetic</a> with constant base and
 bound addresses.</p>
<p>Since we only translate the LSBs, the higher address bits are unused here,
 and would trigger CAD tool warnings about module inputs not driving
 anything.  Thus, we don't select the LSBs from the whole address here, and
 it is up to the enclosing module to feed the Address Translator only the
 necessary LSBs, which is fine since the enclosing module must already know
 the number of LSBs to translate. The output is the translated LSBs in
 consecutive order.</p>

<pre>
`default_nettype none

module <a href="./Address_Translator_Static.html">Address_Translator_Static</a>
#(
    parameter       INPUT_ADDR_BASE     = 0,
    parameter       OUTPUT_ADDR_WIDTH   = 0
)
(
    input   wire    [OUTPUT_ADDR_WIDTH-1:0] input_addr,
    output  reg     [OUTPUT_ADDR_WIDTH-1:0] output_addr
);
</pre>

<p>Let's create the translation table and specify its implementation as LUT
 logic, otherwise it might end up as a Block RAM or asynchronous LUT RAM at
 random, and then the table cannot get optimized into other logic, and will
 likely be too slow. We make the table deep enough to hold all possible LSB
 values, which enables better logic optimization (no special cases for
 unused addresses).</p>

<pre>
    localparam OUTPUT_ADDR_DEPTH = 2**OUTPUT_ADDR_WIDTH;

    (* ramstyle = "logic" *)        // Quartus
    (* ram_style = "distributed" *) // Vivado
    reg [OUTPUT_ADDR_WIDTH-1:0] translation_table [OUTPUT_ADDR_DEPTH-1:0];
</pre>

<p>Now lets construct the translation table entry index <strong>j</strong>. When
 initializing <strong>j</strong>, we must zero-pad the narrow input address up to the
 width of a Verilog integer, else the width mismatch raises warnings at its
 assigment. </p>
<p>We also need a simple loop counter <strong>i</strong> to iterate over the number of
 addresses to translate, which is the entire range described by the LSBs.</p>

<pre>
    integer i;
    integer j;
    localparam INTEGER_WIDTH    = 32;
    localparam PADSIZE          = INTEGER_WIDTH - OUTPUT_ADDR_WIDTH;
    localparam PADDING          = {PADSIZE{1'b0}};
</pre>

<p>We then initialize <strong>j</strong> by slicing out the LSBs from the integer base
 address, and then padding it back to an integer. We then store the
 (strictly incrementing) corresponding LSBs from the <strong>i</strong> counter into each
 translation table entry indexed by <strong>j</strong>, wrapping around to the start of
 the table if necessary.</p>

<pre>
    initial begin
        j = {PADDING, INPUT_ADDR_BASE[OUTPUT_ADDR_WIDTH-1:0]};
        for(i = 0; i < OUTPUT_ADDR_DEPTH; i = i + 1) begin
            translation_table[j] = i[OUTPUT_ADDR_WIDTH-1:0];
            j = (j + 1) % OUTPUT_ADDR_DEPTH;
        end
    end
</pre>

<p>Finally, after all these details, the translation itself is trivial.</p>

<pre>
    always @(*) begin
        output_addr = translation_table[input_addr];
    end

endmodule
</pre>

<hr>
<p><a href="./index.html">Back to FPGA Design Elements</a>
<center><a href="https://fpgacpu.ca/">fpgacpu.ca</a></center>
</body>
</html>

